//! Trap handling functionality
//!
//! For rCore, we have a single trap entry point, namely `__alltraps`. At
//! initialization in [`init()`], we set the `stvec` CSR to point to it.
//!
//! All traps go through `__alltraps`, which is defined in `trap.S`. The
//! assembly language code does just enough work restore the kernel space
//! context, ensuring that Rust code safely runs, and transfers control to
//! [`trap_handler()`].
//!
//! It then calls different functionality based on what exactly the exception
//! was. For example, timer interrupts trigger task preemption, and syscalls go
//! to [`syscall()`].
mod context;
use crate::{batch::run_next_app, syscall};
pub use context::TrapContext;
use core::arch::global_asm;
use riscv::register::{
    scause::{self, Exception, Trap},
    stval,
    stvec::{self, TrapMode},
};

global_asm!(include_str!("trap.S"));

/// 初始化寄存器stvec， 控制 Trap 处理代码的入口地址,内核启动时调用
/// initialize CSR `stvec` as the entry of `__alltraps`
pub fn init() {
    unsafe extern "C" {
        fn __alltraps();
    }
    //stvec寄存器赋值成__alltraps地址
    unsafe {
        stvec::write(__alltraps as usize, TrapMode::Direct);
    }
}

/// handle an interrupt, exception, or system call from user space，异常、系统调用、中断 统一处理函数
#[unsafe(no_mangle)]
pub fn trap_handler(cx: &mut TrapContext) -> &mut TrapContext {
    //读取scause、stval寄存器
    let scause = scause::read(); // get trap cause
    let stval = stval::read(); // get extra value
    match scause.cause() {
        //系统调用trap
        Trap::Exception(Exception::UserEnvCall) => {
            //sret时跳转至ecall指令的下一条指令开始执行
            cx.sepc += 4;
            //a0 ~ a6（x10 ~ x16）传递系统调用参数，a7（x17）传递系统调用ID
            //此处会修改内核栈中TrapContext中a0对应内存，系统调用返回用户态后，仅a0寄存器的值发生变化（返回值），类似函数调用规范，a0调用者保存寄存器
            cx.x[10] = syscall::syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize;
        }
        //各类异常处理
        Trap::Exception(Exception::IllegalInstruction) => {
            println!("[kernel] IllegalInstruction in application, kernel killed it.");
            run_next_app();
        }
        Trap::Exception(Exception::StoreFault) | Trap::Exception(Exception::StorePageFault) => {
            println!("[kernel] PageFault in application, kernel killed it.");
            run_next_app();
        }
        _ => {
            panic!(
                "Unsupported trap {:?}, stval = {:#x}!",
                scause.cause(),
                stval
            );
        }
    }
    cx
}
